package dian.qing.li.demo.security.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.pactera.redis.utils.RedisUtils;
import dian.qing.li.demo.security.bean.RoleParam;
import dian.qing.li.demo.security.constants.CodeKey;
import dian.qing.li.demo.security.constants.Constants;
import dian.qing.li.demo.security.constants.DataType;
import dian.qing.li.demo.security.constants.RedisKey;
import dian.qing.li.demo.security.dto.RoleAuthorityDto;
import dian.qing.li.demo.security.dto.RoleDto;
import dian.qing.li.demo.security.dto.RoleUserDto;
import dian.qing.li.demo.security.dto.UserDto;
import dian.qing.li.demo.security.mapper.RoleAuthorityMapper;
import dian.qing.li.demo.security.mapper.RoleMapper;
import dian.qing.li.demo.security.mapper.RoleUserMapper;
import dian.qing.li.demo.security.mapper.UserMapper;
import dian.qing.li.demo.security.service.RoleService;
import dian.qing.li.demo.security.service.UserService;
import dian.qing.li.demo.security.utils.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-07-01
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RoleDto> implements RoleService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleUserMapper roleUserMapper;

    @Autowired
    private RoleAuthorityMapper roleAuthorityMapper;

    @Autowired
    private UserService userService;

    @Override
    public Page<RoleDto> pages(RoleParam param, Page page) {
        if (param == null) {
            param = new RoleParam();
        }
        if (page == null) {
            page = new Page();
        }
        List<OrderItem> orders = page.getOrders();
        orders.stream().forEach(a -> a.setColumn(CodeUtils.underlineField(a.getColumn())));
        QueryBuilder builder = SqlUtils.builder(param);
        builder.like("role_code", "role_name");
        QueryWrapper query = builder.query();
        return this.page(page, query);
    }

    @Override
    public RoleDto find(Integer id) {
        Assert.isNotNull(id, "[id]不能为空");
        RoleDto role = this.getById(id);
        Assert.isNotNull(role, StringUtils.join("不存在[", id, "]角色详情"));
        return role;
    }

    @Override
    public RoleDto find(String roleCode) {
        Assert.isNotBlank(roleCode, "[roleCode]不能为空");
        RoleDto find = new RoleDto();
        find.setRoleCode(roleCode.trim());
        QueryWrapper<RoleDto> query = new QueryWrapper<>(find);
        RoleDto role = this.getOne(query);
        Assert.isNotNull(role, StringUtils.join("不存在[", roleCode, "]角色详情"));
        return role;
    }

    @Override
    public List<RoleDto> listByUser(String userCode) {
        Assert.isNotBlank(userCode, "[userCode]不能为空");
        try {
            UserDto user = userService.find(userCode);
            QueryWrapper<RoleUserDto> queryRoleUser = new QueryWrapper<>();
            queryRoleUser.select("role_code");
            queryRoleUser.eq("user_code", user.getUserCode());
            queryRoleUser.groupBy("role_code");
            List<RoleUserDto> roleUserDtos = roleUserMapper.selectList(queryRoleUser);
            Set<String> roleCodeSet = roleUserDtos.stream().map(RoleUserDto::getRoleCode).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(roleCodeSet)) {
                return Lists.newArrayList();
            }
            QueryWrapper<RoleDto> queryRole = new QueryWrapper<>();
            queryRole.in("role_code", roleCodeSet);
            queryRole.orderByAsc("sort").orderByDesc("update_time");
            return this.list(queryRole);
        } catch (Exception e) {
            return Lists.newArrayList();
        }
    }

    @Override
    public boolean inserted(RoleDto inserted) {
        Assert.isNotNull(inserted, "[inserted]不能为空");
        // 必填校验
        String[] check = {"roleName", "roleType"};
        ObjUtils.isNotNull(inserted, true, check);
        // 字符串属性trim
        ObjUtils.trim(inserted);
        // 重名校验
        this.checkDuplication(null, inserted.getRoleName());
        // 标准属性设置
        inserted.setId(null);
        inserted.setDeleted(null);
        inserted.setCreateBy(SysUtils.getUsername());
        inserted.setUpdateBy(inserted.getCreateBy());
        // 生成编码
        String redisKey = RedisUtils.redisKey(RedisKey.CODE, CodeKey.ROLE);
        long init = 0;
        if (!RedisUtils.hasKey(redisKey)) {
            Long max = this.getBaseMapper().initCode();
            init = max == null ? 0 : max;
        }
        String numCode = RedisUtils.getNumCode(redisKey, DataType.ROLE.getCode(), init);
        inserted.setRoleCode(numCode);
        return this.save(inserted);
    }

    /**
     * 重名校验
     *
     * @author: liqingdian
     */
    private void checkDuplication(Integer id, String roleName) {
        RoleDto find = new RoleDto();
        find.setRoleName(roleName.trim());
        QueryWrapper<RoleDto> query = new QueryWrapper<>(find);
        if (id != null) {
            query.ne("id", id);
        }
        int count = this.count(query);
        Assert.isFalse(count > 0, StringUtils.join("[", roleName.trim(), "]已经存在，请重新输入"));
    }

    @Override
    public boolean updated(RoleDto updated) {
        Assert.isNotNull(updated, "[updated]不能为空");
        Assert.isNotNull(updated.getId(), "[updated.id]不能为空");
        Assert.isNotNull(updated.getRoleName(), "[updated.roleName]不能为空");
        // 字符串属性trim
        ObjUtils.trim(updated);
        // 设置不允许修改字段为空
        updated.setRoleCode(null);
        updated.setRoleType(null);
        updated.setDeleted(null);
        // 重名校验
        this.checkDuplication(updated.getId(), updated.getRoleName());
        // 设置更新属性
        updated.setUpdateBy(SysUtils.getUsername());
        // 是否禁用，是否删除
        Integer disabled = updated.getDisabled();
        if (disabled != null) {
            if (Constants.YES == disabled) {
                updated.setDisabled(Constants.YES);
            } else {
                updated.setDisabled(Constants.NO);
            }
        }
        return this.updateById(updated);
    }

    @Override
    public boolean disabled(Integer id, boolean disabled) {
        RoleDto role = this.find(id);
        RoleDto updated = new RoleDto();
        updated.setId(id);
        updated.setUpdateBy(SysUtils.getUsername());
        int yes = disabled ? Constants.YES : Constants.NO;
        updated.setDisabled(yes);
        boolean success = true;
        if (yes != role.getDisabled()) {
            success = this.updateById(updated);
        }
        if (disabled) {
            // 刷新用户缓存
            this.refreshUser(role);
        }
        return success;
    }

    @Transactional
    @Override
    public boolean deleted(Integer id) {
        RoleDto role = this.find(id);

        // 删除角色-权限表
        UpdateWrapper<RoleAuthorityDto> deleteRoleAuth = new UpdateWrapper<>();
        deleteRoleAuth.set(Constants.DELETE_FIELD, Constants.YES);
        deleteRoleAuth.eq("role_code", role.getRoleCode());
        roleAuthorityMapper.update(null, deleteRoleAuth);

        // 删除角色-用户表
        UpdateWrapper<RoleUserDto> deleteRoleUser = new UpdateWrapper<>();
        deleteRoleUser.set(Constants.DELETE_FIELD, Constants.YES);
        deleteRoleUser.eq("role_code", role.getRoleCode());
        roleUserMapper.update(null, deleteRoleUser);

        RoleDto deleted = new RoleDto();
        deleted.setId(id);
        deleted.setDeleted(Constants.YES);
        deleted.setUpdateBy(SysUtils.getUsername());
        boolean success = this.updateById(deleted);
        // 刷新用户缓存
        this.refreshUser(role);
        return success;
    }

    /**
     * 刷新用户缓存
     *
     * @param role 角色
     * @author: liqingdian
     */
    private void refreshUser(RoleDto role) {
        // 用户列表
        List<UserDto> userDtos = userService.listByRole(role.getRoleCode());
        Set<String> userCodeSet = userDtos.stream().map(UserDto::getUserCode).collect(Collectors.toSet());
        for (String userCode : userCodeSet) {
            userService.refreshRedis(userCode, false);
        }
    }

    @Transactional
    @Override
    public boolean grantUsers(String roleCode, List<String> userCodes) {
        if (CollectionUtils.isNotEmpty(userCodes)) {
            RoleDto role = this.find(roleCode);
            Set<String> set = userCodes.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
            RoleUserDto inserted;
            for (String userCode : set) {
                inserted = new RoleUserDto();
                inserted.setRoleCode(role.getRoleCode());
                inserted.setUserCode(userCode);
                roleUserMapper.insert(inserted);
            }
            return true;
        }
        return false;
    }

    @Transactional
    @Override
    public boolean cancelUsers(String roleCode, List<String> userCodes) {
        if (CollectionUtils.isNotEmpty(userCodes)) {
            RoleDto role = this.find(roleCode);
            Set<String> set = userCodes.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(set)) {
                UpdateWrapper<RoleUserDto> deleted = new UpdateWrapper<>();
                deleted.set(Constants.DELETE_FIELD, Constants.YES);
                deleted.eq("role_code", role.getRoleCode());
                deleted.in("user_code", set);
                roleUserMapper.update(null, deleted);
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean grantAuths(String roleCode, List<String> authCodes) {
        if (CollectionUtils.isNotEmpty(authCodes)) {
            RoleDto role = this.find(roleCode);
            Assert.isTrue(Constants.ROLE_TYPE_AUTH == role.getRoleType(), "非权限角色不允许权限授权");
            Set<String> set = authCodes.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
            RoleAuthorityDto inserted;
            for (String authCode : set) {
                inserted = new RoleAuthorityDto();
                inserted.setRoleCode(role.getRoleCode());
                inserted.setAuthCode(authCode);
                roleAuthorityMapper.insert(inserted);
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean cancelAuths(String roleCode, List<String> authCodes) {
        if (CollectionUtils.isNotEmpty(authCodes)) {
            RoleDto role = this.find(roleCode);
            Assert.isTrue(Constants.ROLE_TYPE_AUTH == role.getRoleType(), "非权限角色不允许权限授权");
            Set<String> set = authCodes.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(set)) {
                UpdateWrapper<RoleAuthorityDto> deleted = new UpdateWrapper<>();
                deleted.set(Constants.DELETE_FIELD, Constants.YES);
                deleted.eq("role_code", role.getRoleCode());
                deleted.in("auth_code", set);
                roleAuthorityMapper.update(null, deleted);
                return true;
            }
        }
        return false;
    }
}
